package main

import (
	"bytes"
	"fmt"
	"reflect"
	"strings"
	"unicode"
	"unicode/utf8"
	"unsafe"
)

const (
	maxLen = ^uint32(0)
)

func main() {
	fmt.Println("=====Split=====")
	Split()

	fmt.Println("=====Len=====")
	Len()

	fmt.Println("=====中文=====")
	Chinese()

	fmt.Println("=====字符串并不都是 UTF8 文本=====")
	utf8string()

	runeIndex()

	fmt.Println("=====字符串衔接方法=====")
	stringJoin()

	UnsafeStringToBytes()
}

func Split() {
	s := strings.Split("", "")
	// [] 0
	fmt.Println(s, len(s))

	s = strings.Split("abc,abc", "")
	// [a b c , a b c] 7
	fmt.Println(s, len(s))

	s = strings.Split("", ",")
	// [] 1
	fmt.Println(s, len(s))

	s = strings.Split("abc,abc", ",")
	// [abc abc] 2
	fmt.Println(s, len(s))

	s = strings.Split("abc,abc", "|")
	// [abc,abc] 1
	fmt.Println(s, len(s))
}

func Len() {
	// 0
	fmt.Println(len(""))

	// 1
	fmt.Println(len([]string{""}))
}

func Chinese() {
	var str0 = "Hello"
	fmt.Println(str0, " Len ", len(str0))
	fmt.Println(str0, " RuneCountInString ", utf8.RuneCountInString(str0))

	var str1 = "你好"
	fmt.Println(str1, " Len ", len(str1))
	fmt.Println(str1, " RuneCountInString ", utf8.RuneCountInString(str1))

	var str2 = "é" // 法文
	fmt.Println(str2, " Len ", len(str2))
	fmt.Println(str2, " RuneCountInString ", utf8.RuneCountInString(str2))

	fmt.Println("=====range []byte=====")
	for _, s := range []byte(str1) {
		fmt.Println(s)
	}

	fmt.Println("=====range []rune=====")
	for _, s := range []rune(str1) {
		fmt.Printf("%c %v %v \n", s, s, string(s))
	}

	fmt.Printf("你好：%v \n", hasHan("你好"))
	fmt.Printf("hello：%v \n", hasHan("hello"))
	fmt.Printf("123:%v \n", hasHan("123"))
	fmt.Printf("123你好:%v \n", hasHan("123你好"))
}

// 是否有中文
func hasHan(str string) bool {
	for _, char := range str {
		if unicode.In(char, unicode.Han) {
			return true
		}
	}
	return false

}

func utf8string() {
	str1 := "ABC"
	fmt.Println(str1, utf8.ValidString(str1)) // true

	str2 := "A\xfeC"
	fmt.Println(str2, utf8.ValidString(str2)) // false

	str3 := "A\\xfeC"
	fmt.Println(str3, utf8.ValidString(str3)) // true    // 把转义字符转义成字面值

	str4 := "😁"
	fmt.Println(str4, utf8.ValidString(str4)) // true
}

/*
下标循环变量的值并非连续。原因是下标循环变量为字符串中字节的下标，而一个码点可能需要多个字节进行UTF-8编码。
*/
func runeIndex() {
	s := "éक्षिaπ囧"
	for i, rn := range s {
		fmt.Printf("%2v: 0x%x %v \n", i, rn, string(rn))
	}
	fmt.Println(len(s))
}

func UnsafeStringToBytes() {
	buf := unsafeStringToBytes("hello")
	fmt.Println(string(buf))
	fmt.Println(buf)

	// 没法修改，会panic，普通的string转[]byte就不会报错
	//buf[0] = 'a'

	buf = []byte("hello")
	buf[0] = 'a'
	fmt.Println(buf)
}

/*
免去了底层[...]byte复制的好处；[]byte(string)都要内存复制，从而让只读的内存复制到heap or stack上；
坏处是操作这个[]byte时需要注意不能进行修改，否则会触发SIGSEGV，
因为string的data指针指向的数据属于只读的data segment；这个方法只用于只读的string to []byte的场景
*/
func unsafeStringToBytes(str string) []byte {
	if str == "" {
		return nil
	}

	strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
	strBytes := (*(*[maxLen]byte)(unsafe.Pointer(strHeader.Data)))[:len(str):len(str)]

	return strBytes
}

// 字符串衔接方法
func stringJoin() {
	// 常见的 +
	s1 := "hello"
	s2 := " world"

	fmt.Println(s1 + s2)
	fmt.Println(fmt.Sprintf("%s%s", s1, s2))
	fmt.Println(strings.Join([]string{s1, s2}, ","))

	stringBuf := strings.Builder{}
	stringBuf.WriteString(s1)
	stringBuf.WriteString(s2)
	fmt.Println(stringBuf.String())

	byteBuf := bytes.Buffer{}
	byteBuf.WriteString(s1)
	byteBuf.WriteString(s2)
	fmt.Println(byteBuf.String())
}
